/*
    SDIO驱动在linux内核中 存放在 /drivers/mmc目录下
    主要有card / core / host 这三个目录
    card目录下是SD卡驱动，作为上层与SD子系统沟通的桥梁
    core目录下是mmc核心层，主要是抽象出了 mmc sd sdio 通用的部分、
    host目录下是mmc控制器驱动，作为底层与硬件沟通的桥梁
*/

/*
  sdio控制器
  1. SDIO控制器驱动
  2. SDIO卡驱动
  3. SDIO核心层
*/

/*
 [ /include/linux/mmc/host.h ]
struct mmc_host     用来描述卡控制器
struct mmc_card     用来描述卡
struct mmc_driver  用来描述 mmc 卡驱动
struct sdio_func      用来描述 功能设备
struct mmc_host_ops   用来描述卡控制器操作接口函数功能，用于从 主机控制器层向 core 层注册操作函数，从而将core 层与具体的主机控制器隔离。
*/


#include <linux/mmc/host.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/mmc/dw_mci.h>
#include <linux/mmc/dw_mci-exynos.h>
#include <linux/init.c>
#include <linux/module.c>











/*
 * Allocate and initialise a new SDIO function structure.
 * func分配struct sdio_func
 */
struct sdio_func *sdio_alloc_func(struct mmc_card *card)
{
	struct sdio_func *func;

	func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);

	func->card = card;

	device_initialize(&func->dev);

	func->dev.parent = &card->dev;//父节点为card设备
	func->dev.bus = &sdio_bus_type; //bus类型为sdio_bus_type
	func->dev.release = sdio_release_func;

	return func;
}

static struct bus_type sdio_bus_type = {
	.name		= "sdio",
	.dev_attrs	= sdio_dev_attrs,
	.match		= sdio_bus_match,
	.uevent		= sdio_bus_uevent,
	.probe		= sdio_bus_probe,
	.remove		= sdio_bus_remove,
	.pm		= SDIO_PM_OPS_PTR,
};

static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
	struct sdio_driver *sdrv)
{
	const struct sdio_device_id *ids;

	ids = sdrv->id_table;

	if (ids) {
		while (ids->class || ids->vendor || ids->device) { 读取card func中id和driver中id匹配
			if (sdio_match_one(func, ids))
				return ids;
			ids++;
		}
	}

	return NULL;
}








/*
 * Allocate and initialise a new MMC card structure.
 * 分配struct mmc_card
 */
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
{
	struct mmc_card *card;

	card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);


	card->host = host;

	device_initialize(&card->dev);

	card->dev.parent = mmc_classdev(host);
	card->dev.bus = &mmc_bus_type;//设置卡设备bus为mmc_bus_type
	card->dev.release = mmc_release_card;
	card->dev.type = type;

	return card;
}


static struct bus_type mmc_bus_type = {
	.name		= "mmc",
	.dev_attrs	= mmc_dev_attrs,
	.match		= mmc_bus_match,
	.uevent		= mmc_bus_uevent,
	.probe		= mmc_bus_probe,
	.remove		= mmc_bus_remove,
	.pm		= &mmc_bus_pm_ops,
};
/*
 * This currently matches any MMC driver to any MMC card - drivers
 * themselves make the decision whether to drive this card in their
 * probe method.
 */
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
	return 1; //默认返回1，任何mmc card和mmc驱动都能够匹配
}








/*
 * Starting point for SDIO card init.
 * SDIO类型初始化
 */
int mmc_attach_sdio(struct mmc_host *host)
{
	int err, i, funcs;
	u32 ocr;
	struct mmc_card *card;


	//发送cmd5，如果收到响应就是sdio卡，否则不是直接返回； 收到响应为R4指示sdio卡使用的电压
	err = mmc_send_io_op_cond(host, 0, &ocr);
	if (err)
		return err;
    //设置mmc host中bus_ops为sdio ops
	mmc_attach_bus(host, &mmc_sdio_ops);
	if (host->ocr_avail_sdio)
		host->ocr_avail = host->ocr_avail_sdio;

	/*
	 * Sanity check the voltages that the card claims to
	 * support.
	 */
	 // cmd5时返回电压
	if (ocr & 0x7F) {
		pr_warning("%s: card claims to support voltages "
		       "below the defined range. These will be ignored.\n",
		       mmc_hostname(host));
		ocr &= ~0x7F;
	}
    //设置电压值，需要sdio控制器支持的电压和sdio卡匹配
	host->ocr = mmc_select_voltage(host, ocr);
    //初始化卡，里面先分配card类型结构，struct mmc_card card = mmc_alloc_card(host, NULL);并判断sdio对应device是否为存储还是其它功能（wifi，gps等），设置card->type = MMC_TYPE_SD_COMBO;或card->type = MMC_TYPE_SDIO;并且获取mmc_send_relative_addr(host, &card->rca);设备地址保存到card->rca
	err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
	card = host->card;
	/*
	 * The number of functions on the card is encoded inside
	 * the ocr.
	 */
	funcs = (ocr & 0x70000000) >> 28; //sdio device上具备的功能个数，例如wifi设备
	card->sdio_funcs = 0;
	/*
	 * Initialize (but don't add) all present functions.
	 */
	for (i = 0; i < funcs; i++, card->sdio_funcs++) {
			//初始化化sdio 对应卡设备上功能，分配了struct sdio_func， sdio_alloc_func(card);
			err = sdio_init_func(host->card, i + 1);
	}
	/*
	 * First add the card to the driver model...
	 */
	mmc_release_host(host);
	err = mmc_add_card(host->card); //把card设备注册到系统中
	/*
	 * ...then the SDIO functions.
	 */
	for (i = 0;i < funcs;i++) {
		err = sdio_add_func(host->card->sdio_func[i]); //把func功能注册到设备上
		if (err)
			goto remove_added;
	}
	mmc_claim_host(host);
	return 0;
}


/*
 * Try to initialise the card at the given frequency.
 * 卡检测
*/
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
	host->f_init = freq;

	//host上电
	mmc_power_up(host);

	/*
	 * Some eMMCs (with VCCQ always on) may not be reset after power up, so
	 * do a hardware reset if possible.
	 */
	mmc_hw_reset_for_init(host); //复位硬件

	/*
	 * sdio_reset sends CMD52 to reset card.  Since we do not know
	 * if the card is being re-initialized, just send it.  CMD52
	 * should be ignored by SD/eMMC cards.
	 */
    //如果目标卡是纯SD卡则目标卡不会应答,一般主机host的寄存器会报错，但是这个无关紧要，可以不理它。如果目标卡是纯SDIO卡，那么这里就是复位SDIO卡，通过命令CMD52来实现的。③如果目标卡是SD卡和SDIO卡的组合卡，则需要先发送CMD52来复位SDIO卡，再复位SD卡，因为CMD52要先于CMD0发送。
	sdio_reset(host);
	mmc_go_idle(host);//发送CMD0，让设备进入IDLE模式

	mmc_send_if_cond(host, host->ocr_avail);//发送CMD8，获取该卡所支持的电压值

	/* Order's important: probe SDIO, then SD, then MMC */
	//识别卡类型
   //首先发送 CMD5。如果收到一个响应，那么该卡是 SDIO。否则发送 ACMD41；如果收到一个响应，那么该卡是 SD。 否则，该卡是 MMC。
	if (!mmc_attach_sdio(host))
		return 0;
	if (!mmc_attach_sd(host))
		return 0;
	if (!mmc_attach_mmc(host))
		return 0;

	mmc_power_off(host);
	return -EIO;
}




static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
{
	struct dw_mci *host = dev_id;
	u32 pending;
	int i;
    /* 获取硬件设备的中断状态寄存器 */
	pending = mci_readl(host, MINTSTS); /* read-only mask reg */

	if (pending) {

		/*
		 * DTO fix - version 2.10a and below, and only if internal DMA
		 * is configured.
		 */
		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) 
        {
			if (!pending &&((mci_readl(host, STATUS) >> 17) & 0x1fff))
				pending |= SDMMC_INT_DATA_OVER;
		}

		if (pending & SDMMC_INT_CMD_DONE) {
			u32 cmd = mci_readl(host, CMD) & 0x3f;
			if (cmd == SD_SWITCH_VOLTAGE &&
				!(mci_readl(host, STATUS) & SDMMC_DATA_BUSY)) {
				pending |= SDMMC_INT_RTO;
			}
		}

		if (pending & SDMMC_INT_HLE) {
			mci_writel(host, RINTSTS, SDMMC_INT_HLE);
			host->cmd_status = pending;
			tasklet_schedule(&host->tasklet);
		}

		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
			host->cmd_status = pending;
			smp_wmb();
			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
		}

		if (pending & SDMMC_INT_VOLT_SW) {
			u32 cmd = mci_readl(host, CMD) & 0x3f;
			if (cmd == SD_SWITCH_VOLTAGE) {
				mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SW);
				dw_mci_cmd_interrupt(host, pending);
			}
		}

		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
			/* if there is an error report DATA_ERROR */
			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
			host->data_status = pending;
			smp_wmb();
			set_bit(EVENT_DATA_ERROR, &host->pending_events);
			tasklet_schedule(&host->tasklet);
		}

		if (pending & SDMMC_INT_DATA_OVER) {
			if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
				del_timer(&host->dto_timer);

			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
			if (!host->data_status)
				host->data_status = pending;
			smp_wmb();
			if (host->dir_status == DW_MCI_RECV_STATUS) {
				if (host->sg != NULL)
					dw_mci_read_data_pio(host, true);
			}
			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
			tasklet_schedule(&host->tasklet);
		}

		if (pending & SDMMC_INT_RXDR) {
			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
			if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) {
				dw_mci_read_data_pio(host, false);
			} else {
				if (host->hw_mmc_id == DWMMC_SD_ID && !host->sg) {
					printk(KERN_DEBUG"mmc%d:debug error:host.sg=%p.cmd%d\n",
							host->hw_mmc_id, host->sg,mci_readl(host, CMD) & 0x3F);
					dw_mci_fifo_reset(host->dev, host);
					dw_mci_ciu_reset(host->dev, host);
				}
			}
		}

		if (pending & SDMMC_INT_TXDR) {
			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
			if (host->dir_status == DW_MCI_SEND_STATUS && host->sg)
				dw_mci_write_data_pio(host);
		}

		if (pending & SDMMC_INT_CMD_DONE) {
			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
			dw_mci_cmd_interrupt(host, pending);
		}

		if (pending & SDMMC_INT_CD) { //卡检测中
			mci_writel(host, RINTSTS, SDMMC_INT_CD);
			queue_work(host->card_workqueue, &host->card_work);//唤醒card_workqueue队列工作
		}

		/* Handle SDIO Interrupts */
		for (i = 0; i < host->num_slots; i++) {
			struct dw_mci_slot *slot = host->slot[i];
			if (pending & SDMMC_INT_SDIO(i)) {
				mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));
				mmc_signal_sdio_irq(slot->mmc);
			}
		}

	}

#ifdef CONFIG_MMC_DW_IDMAC
	/* Handle DMA interrupts */
	pending = mci_readl(host, IDSTS);
	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
		host->dma_ops->complete(host);
	}
#endif

	return IRQ_HANDLED;
}


void mmc_start_host(struct mmc_host *host)
{
	host->f_init = max(freqs[0], host->f_min);
	host->rescan_disable = 0;
	if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
		mmc_power_off(host);
	else
		mmc_power_up(host);
	mmc_detect_change(host, 0); //控制器开始工作后，主动探测卡是否存在
}



int dw_mci_probe(struct dw_mci *host)
{
	const struct dw_mci_drv_data *drv_data = host->drv_data;
	int width, i, ret = 0;
	u32 fifo_size;
	int init_slots = 0;
	u32 msize;


	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);//注册tasklet中断上半部，处理各种中断
	host->card_workqueue = alloc_workqueue("dw-mci-card", WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
	if (!host->card_workqueue)
		goto err_dmaunmap;
	INIT_WORK(&host->card_work, dw_mci_work_routine_card); //注册workqueue中断上半部，主要处理卡检测
	ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, //注册硬件中断
			       host->irq_flags, "dw-mci", host);

	/* We need at least one slot to succeed */
	for (i = 0; i < host->num_slots; i++) {
		ret = dw_mci_init_slot(host, i); //初始化slot
		if (ret)
			dev_dbg(host->dev, "slot %d init failed\n", i);
		else
			init_slots++;
	}
}




static int dw_mci_exynos_probe(struct platform_device *pdev)
{
	const struct dw_mci_drv_data *drv_data;
	const struct of_device_id *match;

	match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
	drv_data = match->data; //driver data:exynos_drv_data
	return dw_mci_pltfm_register(pdev, drv_data);
}





/*驱动注册*/
static const struct dw_mci_drv_data exynos_drv_data = {
	.caps			= exynos_dwmmc_caps,
	.init			= dw_mci_exynos_priv_init,
	.setup_clock		= dw_mci_exynos_setup_clock,
	.prepare_command	= dw_mci_exynos_prepare_command,
	.set_ios		= dw_mci_exynos_set_ios,
	.parse_dt		= dw_mci_exynos_parse_dt,
};

static const struct of_device_id dw_mci_exynos_match[] = {
	{ .compatible = "samsung,exynos4412-dw-mshc",
			.data = &exynos_drv_data, },
	{ .compatible = "samsung,exynos5250-dw-mshc",
			.data = &exynos_drv_data, },
	{},
};
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
static struct platform_driver dw_mci_exynos_pltfm_driver = {
	.probe		= dw_mci_exynos_probe,
	.remove		= __exit_p(dw_mci_pltfm_remove),
	.driver		= {
		.name		= "dwmmc_exynos",
		.of_match_table	= dw_mci_exynos_match,
		.pm		= &dw_mci_pltfm_pmops,
	},
};

module_platform_driver(dw_mci_exynos_pltfm_driver); //驱动注册
